#ifndef DEBUGGERGDB_H
#define DEBUGGERGDB_H

#include <map>
#include <sdk_events.h>
#include <M_Code_plugin.h>
#include <loggers.h>
#include <pipedprocess.h>
#include <wx/regex.h>
#include "remotedebugging.h"
#include "debuggerstate.h"
#include "debugger_defs.h"

extern const wxString g_EscapeChar;

class M_Code_Project;
class TiXmlElement;
class DebuggerDriver;
class DebuggerCmd;
class Compiler;
struct TestIfBelogToProject;
class DebuggerConfiguration;

class DebuggerGDB : public cbDebuggerPlugin {
    DebuggerState m_State;
  public:
    DebuggerGDB();
    ~DebuggerGDB();

    cbConfigurationPanel* GetProjectConfigurationPanel( wxWindow* parent, M_Code_Project* project );
    void OnAttachReal(); // fires when the plugin is attached to the application
    void OnReleaseReal( bool appShutDown );
    bool SupportsFeature( cbDebuggerFeature::Flags flag );

    cbDebuggerConfiguration* LoadConfig( const ConfigManagerWrapper &config );

    DebuggerConfiguration& GetActiveConfigEx();

    void RunCommand( int cmd );

    cb::shared_ptr<cbBreakpoint> AddBreakpoint( const wxString& filename, int line );
    cb::shared_ptr<cbBreakpoint> AddDataBreakpoint( const wxString& dataExpression );
    int GetBreakpointsCount() const;
    cb::shared_ptr<cbBreakpoint> GetBreakpoint( int index );
    cb::shared_ptr<const cbBreakpoint> GetBreakpoint( int index ) const;
    void UpdateBreakpoint( cb::shared_ptr<cbBreakpoint> breakpoint );
    void DeleteBreakpoint( cb::shared_ptr<cbBreakpoint> breakpoint );
    void DeleteAllBreakpoints();
    void ShiftBreakpoint( int index, int lines_to_shift );
    void EnableBreakpoint( cb::shared_ptr<cbBreakpoint> breakpoint, bool enable );

    // stack frame calls;
    int GetStackFrameCount() const;
    cb::shared_ptr<const cbStackFrame> GetStackFrame( int index ) const;
    void SwitchToFrame( int number );
    int GetActiveStackFrame() const;

    // threads
    int GetThreadsCount() const;
    cb::shared_ptr<const cbThread> GetThread( int index ) const;
    bool SwitchToThread( int thread_number );

    bool Debug( bool breakOnEntry );
    void Continue();
    void Next();
    void NextInstruction();
    void StepIntoInstruction();
    void Step();
    void StepOut();
    bool RunToCursor( const wxString& filename, int line, const wxString& line_text );
    void SetNextStatement( const wxString& filename, int line );
    void Break();
    void Stop();
    bool Validate( const wxString& line, const char cb );
    bool IsRunning() const { return m_pProcess; }
    bool IsStopped() const;
    bool IsBusy() const;
    bool IsTemporaryBreak() const {return m_TemporaryBreak;}
    int GetExitCode() const { return m_LastExitCode; }

    cb::shared_ptr<cbWatch> AddWatch( const wxString& symbol );
    void DeleteWatch( cb::shared_ptr<cbWatch> watch );
    bool HasWatch( cb::shared_ptr<cbWatch> watch );
    void ShowWatchProperties( cb::shared_ptr<cbWatch> watch );
    bool SetWatchValue( cb::shared_ptr<cbWatch> watch, const wxString &value );
    void ExpandWatch( cb::shared_ptr<cbWatch> watch );
    void CollapseWatch( cb::shared_ptr<cbWatch> watch );
    void UpdateWatch( cb::shared_ptr<cbWatch> watch );

    void AddWatchNoUpdate( const cb::shared_ptr<GDBWatch> &watch );

    void OnWatchesContextMenu( wxMenu &menu, const cbWatch &watch, wxObject *property, int &disabledMenus );

    void GetCurrentPosition( wxString &filename, int &line );
    void RequestUpdate( DebugWindows window );

    void AttachToProcess( const wxString& pid );
    void DetachFromProcess();
    bool IsAttachedToProcess() const;

    void SendCommand( const wxString& cmd, bool debugLog );
    void DoSendCommand( const wxString& cmd );

    DebuggerState& GetState() { return m_State; }

    void OnConfigurationChange( bool isActive );

    wxArrayString& GetSearchDirs( M_Code_Project* prj );
    RemoteDebuggingMap& GetRemoteDebuggingMap( M_Code_Project* project = 0 );

    void OnProjectLoadingHook( M_Code_Project* project, TiXmlElement* elem, bool loading );

    void OnValueTooltip( const wxString &token, const wxRect &evalRect );
    bool ShowValueTooltip( int style );

    static void ConvertToGDBFriendly( wxString& str );
    static void ConvertToGDBFile( wxString& str );
    static void ConvertToGDBDirectory( wxString& str, wxString base = _T( "" ), bool relative = true );
    static void StripQuotes( wxString& str );

    void DebuggeeContinued();

  protected:
    M_Code_Project* GetProject() { return m_pProject; }
    void ResetProject() { m_pProcess = NULL; }
    void ConvertDirectory( wxString& str, wxString base, bool relative );
    void CleanupWhenProjectClosed( M_Code_Project *project );
    bool CompilerFinished( bool compilerFailed, StartType startType );
  protected:
    void AddSourceDir( const wxString& dir );
  private:
    void ParseOutput( const wxString& output );
    void DoWatches();
    void MarkAllWatchesAsUnchanged();
    int LaunchProcess( const wxString& cmd, const wxString& cwd );
    int DoDebug( bool breakOnEntry );
    void DoBreak( bool temporary );

    void OnAddSymbolFile( wxCommandEvent& event );
    void DeleteAllProjectBreakpoints( M_Code_Project* project );
    void OnBuildTargetSelected( CodeBlocksEvent& event );
    void OnGDBOutput( wxCommandEvent& event );
    void OnGDBError( wxCommandEvent& event );
    void OnGDBTerminated( wxCommandEvent& event );
    void OnIdle( wxIdleEvent& event );
    void OnTimer( wxTimerEvent& event );
    void OnShowFile( wxCommandEvent& event );
    void OnCursorChanged( wxCommandEvent& event );

    void SetupToolsMenu( wxMenu &menu );
    void KillConsole();
    void CheckIfConsoleIsClosed();

    void OnInfoFrame( wxCommandEvent& event );
    void OnInfoDLL( wxCommandEvent& event );
    void OnInfoFiles( wxCommandEvent& event );
    void OnInfoFPU( wxCommandEvent& event );
    void OnInfoSignals( wxCommandEvent& event );

    void OnMenuWatchDereference( wxCommandEvent& event );

    void OnUpdateTools( wxUpdateUIEvent &event );
    void OnPrintElements( wxCommandEvent &event );

    void OnUpdateCatchThrow( wxUpdateUIEvent &event );
    void OnCatchThrow( wxCommandEvent &event );
  private:
    PipedProcess* m_pProcess;
    bool m_LastExitCode;
    int m_Pid;
    int m_PidToAttach; // for "attach to process"
    wxRect m_EvalRect;
    wxTimer m_TimerPollDebugger;
    bool m_NoDebugInfo;

    bool m_StoppedOnSignal;

    // extra dialogs
    M_Code_Project* m_pProject; // keep the currently debugged project handy
    wxString m_ActiveBuildTarget;

    // per-project debugger search-dirs
    typedef std::map<M_Code_Project*, wxArrayString> SearchDirsMap;
    SearchDirsMap m_SearchDirs;

    typedef std::map<M_Code_Project*, RemoteDebuggingMap> ProjectRemoteDebuggingMap;
    ProjectRemoteDebuggingMap m_RemoteDebugging;

    int m_HookId;
    // Linux console support
    bool     m_bIsConsole;
    bool     m_stopDebuggerConsoleClosed;
    int      m_nConsolePid;

    bool m_Canceled;
    bool m_TemporaryBreak;

    WatchesContainer m_watches;
    cb::shared_ptr<GDBWatch> m_localsWatch, m_funcArgsWatch;
    wxString m_watchToDereferenceSymbol;
    wxObject *m_watchToDereferenceProperty;

    friend struct TestIfBelongToProject;

    int m_printElements;

    DECLARE_EVENT_TABLE()
};

#endif
